#ifndef XGE_GRAPHICS_G_COLORS_H_
#define XGE_GRAPHICS_G_COLORS_H_
#include "xge_common_config.h"
#include "xfloathelper.h"
#include "g_common.h"
#include "xassert.h"
// for direct x need the type DWORD 

namespace xge{
namespace graphics{
  // Useful helper function for color operation .
  FORCEINLINE channel_t GetColorAlphaChannel  ( color_t hardware_color ) ;
  FORCEINLINE channel_t GetColorRedChannel    ( color_t hardware_color ) ;
  FORCEINLINE channel_t GetColorBlueChannel   ( color_t hardware_color );
  FORCEINLINE channel_t GetColorGreenChannel  ( color_t hardware_color );
  FORCEINLINE void SetColorAlphaChannel ( color_t* hardware_color , channel_t alpha );
  FORCEINLINE void SetColorRedChannel   ( color_t* haredware_color , channel_t red );
  FORCEINLINE void SetColorBlueChannel  ( color_t* hardware_color , channel_t blue );
  FORCEINLINE void SetColorGreenChannel ( color_t* hardware_color , channel_t green );

  FORCEINLINE channel_t ClampColorChannel( chadiff_t channel );
  FORCEINLINE channel_t ClampColorChannelUpper( channel_t channel );
  FORCEINLINE channel_t ClampColorChannelLower( chadiff_t channel );

  FORCEINLINE color_t MakeHardwareColor( channel_t alpha , channel_t red , channel_t green , channel_t blue );

  // A simple class for representing the color stuff
  class Color {
  public:
    Color( color_t hardware_color_value ) 
      : hardware_color_(hardware_color_value){}
    Color( channel_t alpha , channel_t red , channel_t green , channel_t blue ) 
      : hardware_color_(MakeHardwareColor(alpha,red,green,blue)){}
    Color( const Color& other_color ) 
      : hardware_color_(other_color.hardware_color_) {}
    Color& operator = ( const Color& other ) {
      hardware_color_ = other.hardware_color_;
      return *this;
    }
    Color():hardware_color_(Color::kBlack){}
    // Export static color variable
    static const Color kRed;
    static const Color kGreen;
    static const Color kBlue;
    static const Color kBlack;
    static const Color kWhite;
  public:
    // operator implementation 
    // Plus implementation
    Color operator + ( const Color& inst ) const {
      return Color( 
        ClampColorChannelUpper( alpha() + inst.alpha() ),
        ClampColorChannelUpper( red()+inst.red()),
        ClampColorChannelUpper( green()+inst.green() ),
        ClampColorChannelUpper( blue()+inst.blue() )
        );
    }
    Color& operator +=( const Color& inst ) {
      return *this = *this+inst;
    }
    // Minus implementation
    Color operator - ( const Color& inst ) const {
      return Color(
        ClampColorChannelLower( (chadiff_t)(alpha()-inst.alpha()) ),
        ClampColorChannelLower( (chadiff_t)(red()-inst.red()) ),
        ClampColorChannelLower( (chadiff_t)(green()-inst.green()) ),
        ClampColorChannelLower( (chadiff_t)(blue()-inst.blue()) )
        );
    }
    Color& operator -=( const Color& inst ) {
      return *this = *this-inst;
    }
    // Scalar multiply implementation
    Color operator * ( const float scalar ) const {
      XASSERT( scalar >=0.0f );
      return Color(
        ClampColorChannelUpper( (channel_t)(scalar*alpha()) ),
        ClampColorChannelUpper( (channel_t)(scalar*red()) ),
        ClampColorChannelUpper( (channel_t)(scalar*green()) ),
        ClampColorChannelUpper( (channel_t)(scalar*blue()) )
        );
    }
    Color& operator *=( const float scalar ) {
      return *this = *this*scalar;
    }
    // Scalar divide implementation 
   Color operator / ( const float scalar ) const {
      XASSERT( !FLOAT_ZERO(scalar) && scalar > 0.0f );
      return Color(
        ClampColorChannelUpper( (channel_t)(alpha()/scalar) ),
        ClampColorChannelUpper( (channel_t)(red()/scalar) ),
        ClampColorChannelUpper( (channel_t)(green()/scalar) ),
        ClampColorChannelUpper( (channel_t)(blue()/scalar)) 
        );
    }
    Color& operator /=( const float scalar ) {
      return *this = *this/scalar;
    }

    operator color_t() const {
      return hardware_color();
    }
  public:
    channel_t alpha() const {
      return GetColorAlphaChannel(hardware_color_);
    }
    void set_alpha( channel_t alpha )  {
      SetColorAlphaChannel(&hardware_color_,alpha);
    }
    channel_t red() const {
      return GetColorRedChannel(hardware_color_);
    }
    void set_red( channel_t red )  {
      SetColorRedChannel(&hardware_color_,red);
    }
    channel_t green() const {
      return GetColorGreenChannel(hardware_color_);
    }
    void set_green( channel_t green ) {
      SetColorGreenChannel(&hardware_color_,green);
    }
    channel_t blue() const {
      return GetColorBlueChannel(hardware_color_);
    }
    void set_blue( channel_t blue )  {
      SetColorBlueChannel(&hardware_color_,blue);
    }

    void set_hardware_color( color_t color ) {
      hardware_color_ = color;
    }

    color_t hardware_color() const {
      return hardware_color_;
    }


  private: 
    // We just use only one color_t to
    // store the hardware_color 
    // this can make this Class as POD type
    color_t hardware_color_;

  };
  // XColorStream represents a color stream
  class ColorStream {
  public:
    // The CTor of this colors , it should process a buffer 
    // for fetching the color stuff here .
    ColorStream( void* buffer , size_t width , size_t height ) 
      : internal_color_buffer_(buffer) , width_(width) , height_(height) {}
    // default color stream 
    ColorStream():internal_color_buffer_(0),width_(0),height_(0){}
  public:
    size_t width() const {
      return width_;
    }
    size_t height() const {
      return height_;
    }

    Color GetColor( size_t x , size_t y ) const;
    void SetColor( const Color& color );
    // testing if the buffer is empty or not
    bool empty() const {
      return internal_color_buffer_ == NULL;
    }
  private:
    // internal color buffer 
    // typically a colors' internal buffer will
    // return by the Texture or something like 
    // download from the GPU VRAM by PCI-E bus .
    // The deletion( Unlock ) will be called
    // manually by the user of those Texture user
    // so it won't take charge of its creation
    // and deletion .
    void* internal_color_buffer_;
    // width and height represents the pixel rectangle
    size_t width_;
    size_t height_;
  };

  //==================Inline function implementation 

  FORCEINLINE channel_t GetColorAlphaChannel( color_t hardware_color ) {
    return (channel_t)( hardware_color >> 24 );
  }

  FORCEINLINE channel_t GetColorRedChannel( color_t hardware_color ) {
    return (channel_t)( (hardware_color >> 16) & 0xff );
  }

  FORCEINLINE channel_t GetColorBlueChannel( color_t hardware_color ) {
    return (channel_t)( hardware_color & 0xff );
  }

  FORCEINLINE channel_t GetColorGreenChannel( color_t hardware_color ) {
    return (channel_t)( (hardware_color >>8) & 0xff ); 
  }

  FORCEINLINE void SetColorAlphaChannel( color_t* hardware_color , channel_t alpha ) {
    (*hardware_color) &= 0x00ffffff ;
    (*hardware_color) += (color_t)(alpha<<24);
  }

  FORCEINLINE void SetColorBlueChannel( color_t* hardware_color , channel_t blue ) {
    (*hardware_color) &=0xffffff00;
    (*hardware_color) +=(color_t)(blue);
  }

  FORCEINLINE void SetColorRedChannel( color_t* haredware_color , channel_t red ) {
    (*haredware_color) &=0xff00ffff;
    (*haredware_color) +=(color_t)(red>>16);
  }

  FORCEINLINE void SetColorGreenChannel( color_t* hardware_color , channel_t green ) {
    (*hardware_color) &=0xffff00ff;
    (*hardware_color) +=(color_t)(green>>8);
  }

  FORCEINLINE color_t MakeHardwareColor( channel_t alpha , channel_t red , channel_t green , channel_t blue ) {
    return (color_t)( (alpha<<24) + (red<<16) + (green<<8) + blue );
  }

  FORCEINLINE channel_t ClampColorChannel( chadiff_t channel) {
    if( channel > 255 ) 
      return 255; 
    else if( channel <0 )
      return 0;
    return channel;
  }

  FORCEINLINE channel_t ClampColorChannelUpper( channel_t channel ) {
    if( channel > 255 )
      return 255;
    else
      return channel;
  }

  FORCEINLINE channel_t ClampColorChannelLower( chadiff_t channel ) {
    if( channel <0 )
      return 0;
    else
      return channel;
  }

}//namespace graphics
}//namespace xge


#endif// XGE_GRAPHICS_G_COLORS_H_